package org.light.domain;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.light.core.Writeable;
import org.light.exception.ValidateException;
import org.light.utils.DomainUtil;
import org.light.utils.FieldUtil;
import org.light.utils.MethodUtil;
import org.light.utils.StringUtil;
import org.light.utils.WriteableUtil;
import org.light.verb.FindById;

public class DummyDaoImpl extends DaoImpl {
	private static final long serialVersionUID = -7376733796370937661L;

	@Override
	public String generateDaoImplString() throws ValidateException{
		if (this.domain instanceof org.light.domain.Enum) {
			return generateEnumDummyDaoImplStatements().getContent();
		}else {
			return generateDummyDaoImplStatements().getContent();
		}
	}	

	public StatementList generateDummyDaoImplStatements() throws ValidateException{
		List<Writeable> sList = new ArrayList<Writeable>();
		
		Set<String> imports = this.generateImportStrings();
		imports.add("super::Table");
		imports.add("super::"+this.domain.getCapFirstDomainNameWithSuffix());
		imports.add("super::"+this.domain.getCapFirstDomainName()+"QueryRequest");
		imports.add("super::CountNum");
		imports.add("lazy_static::lazy_static");
		imports.add("std::sync::Mutex");
		imports.add("serde::{Deserialize, Serialize}");
		imports.add("sqlx::Error");
		
		for (ManyToMany mtm : this.domain.getManyToManies()) {
			FindById findSlave = new FindById(mtm.getSlave());
			imports.add("crate::"+this.domain.getServiceimplSuffix()+"::"+mtm.getSlave().getSnakeDomainName()+"_service::"+StringUtil.getSnakeName(findSlave.getVerbName()));
		}

		if (this.domain.containsDateTime()) {
			imports.add("chrono::NaiveDate");
			imports.add("chrono::NaiveDateTime");
			imports.add("crate::"+this.domain.getDomainSuffix()+"::my_date_time_format::DATE_TIME_FORMAT");
			imports.add("crate::"+this.domain.getDomainSuffix()+"::my_date_format::DATE_FORMAT");
		}
		
		imports.addAll(this.classImports);
		
		sList.add(new Statement(100L,0,"#![allow(unused_imports,dead_code,unused_mut)]"));
		StatementList sl = DomainUtil.generateImportStatements(imports);
		sl.setSerial(1000L);
		sl.setIndent(0);
		sList.add(sl);
		
		sList.add(new Statement(6000L,0,"lazy_static! {"));
		sList.add(new Statement(7000L,1,"static ref DB:Mutex<Vec<"+this.domain.getCapFirstDomainNameWithSuffix()+">> = Mutex::new(init_db());"));
		long serial = 8000L;
		for (ManyToMany mtm : this.domain.getManyToManies()) {
			sList.add(new Statement(serial,1,"static ref "+mtm.getMaster().getSnakeDomainName().toUpperCase()+"_"+StringUtil.getSnakeName(mtm.getSlaveAlias()).toUpperCase()+"_LINK_DB:Mutex<Vec<Link"+mtm.getMaster().getCapFirstDomainName()+StringUtil.capFirst(mtm.getSlaveAlias())+this.domain.getDomainNamingSuffix()+">> = Mutex::new(init_"+mtm.getMaster().getSnakeDomainName()+"_"+StringUtil.getSnakeName(mtm.getSlaveAlias())+"_link_db());"));
			serial += 1000L;
		}
		
		sList.add(new Statement(serial,0,"}"));
		sList.add(new Statement(serial+500L,0,""));
		
		sList.add(new Statement(serial+1000L,0,"fn init_db()->Vec<"+this.domain.getCapFirstDomainNameWithSuffix()+">{"));
		sList.add(new Statement(serial+2000L,1,"let mut _db:Vec<"+this.domain.getCapFirstDomainNameWithSuffix()+"> = vec![];"));
		serial += 3000L;
		for (Domain d: this.domain.getDummyDb()) {
			sList.add(new Statement(serial,1,"_db.push("+this.domain.getCapFirstDomainNameWithSuffix()+" {"));
			serial += 1000L;
			for (Field f: d.getFields()) {
				sList.add(new Statement(serial,2,f.getSnakeFieldName()+": "+FieldUtil.findTypeValueToken(f)+","));
				serial += 1000L;
			}
			sList.add(new Statement(serial,1,"});"));
			serial += 1000L;
		}		
		sList.add(new Statement(serial,1,"_db"));
		sList.add(new Statement(serial+1000L,0,"}"));
		sList.add(new Statement(serial+2000L,0,""));
		
		serial += 3000L;
		for (ManyToMany mtm:this.domain.getManyToManies()) {
			sList.add(new Statement(serial+1000L,0,"#[derive(Serialize, Deserialize, Clone, Debug)]"));
			sList.add(new Statement(serial+2000L,0,"#[serde(rename_all = \"camelCase\")]"));
			sList.add(new Statement(serial+3000L,0,"pub struct Link"+mtm.getMaster().getCapFirstDomainName()+StringUtil.capFirst(mtm.getSlaveAlias())+this.domain.getDomainNamingSuffix()+" {"));
			sList.add(new Statement(serial+4000L,1,"pub "+mtm.getMaster().getSnakeDomainName()+"_id: i64,"));
			sList.add(new Statement(serial+5000L,1,"pub "+StringUtil.getSnakeName(mtm.getSlaveAlias())+"_id: i64,"));
			sList.add(new Statement(serial+6000L,0,"}"));
			sList.add(new Statement(serial+7000L,0,""));
			
			sList.add(new Statement(serial+8000L,0,"fn init_"+mtm.getMaster().getSnakeDomainName()+"_"+StringUtil.getSnakeName(mtm.getSlaveAlias())+"_link_db()->Vec<Link"+mtm.getMaster().getCapFirstDomainName()+StringUtil.capFirst(mtm.getSlaveAlias())+this.domain.getDomainNamingSuffix()+">{"));
			sList.add(new Statement(serial+9000L,1,"let mut _db:Vec<Link"+mtm.getMaster().getCapFirstDomainName()+StringUtil.capFirst(mtm.getSlaveAlias())+this.domain.getDomainNamingSuffix()+"> = vec![];"));
			
			serial += 10000L;
			
			List<ManyToMany> dataMtms = DomainUtil.filterMtms(this.domain.getDummyDb(),mtm);
			for (ManyToMany dmtm: dataMtms) {
				for (String slaveValue:dmtm.getSlaveValuesList()) {
					sList.add(new Statement(serial,1,"_db.push(Link"+mtm.getMaster().getCapFirstDomainName()+StringUtil.capFirst(mtm.getSlaveAlias())+this.domain.getDomainNamingSuffix()+" {"));
					sList.add(new Statement(serial+1000L,2,dmtm.getMaster().getSnakeDomainName()+"_id: "+dmtm.getMasterValue()+","));
					sList.add(new Statement(serial+2000L,2,StringUtil.getSnakeName(dmtm.getSlaveAlias())+"_id: "+slaveValue+","));
					sList.add(new Statement(serial+3000L,1,"});"));
					serial += 4000L;
				}
			}		
			sList.add(new Statement(serial,1,"_db"));
			sList.add(new Statement(serial+500L,0,"}"));
			sList.add(new Statement(serial+700L,0,""));
			sList.add(new Statement(serial+1000L,0,"pub fn get_"+mtm.getMaster().getSnakeDomainName()+"_"+StringUtil.getSnakeName(mtm.getSlaveAlias())+"_link_db()-> Vec<Link"+mtm.getMaster().getCapFirstDomainName()+StringUtil.capFirst(mtm.getSlaveAlias())+this.domain.getDomainNamingSuffix()+"> {"));
			sList.add(new Statement(serial+2000L,1,mtm.getMaster().getSnakeDomainName().toUpperCase()+"_"+StringUtil.getSnakeName(mtm.getSlaveAlias()).toUpperCase()+"_LINK_DB.lock().unwrap().to_vec()"));
			sList.add(new Statement(serial+3000L,0,"}"));
			sList.add(new Statement(serial+4000L,0,""));
			serial += 5000L;
		}
			
		sList.add(new Statement(serial+1000L,0,"pub fn get_db()-> Vec<"+this.domain.getCapFirstDomainNameWithSuffix()+"> {"));
		sList.add(new Statement(serial+2000L,1,"DB.lock().unwrap().to_vec()"));
		sList.add(new Statement(serial+3000L,0,"}"));
		sList.add(new Statement(serial+4000L,0,""));
		
		sList.add(new Statement(serial+5000L,0,"fn max_id()->i64{"));
		sList.add(new Statement(serial+6000L,1,"let mut max:i64 = 0;"));
		sList.add(new Statement(serial+7000L,1,"for "+this.domain.getSnakeDomainName()+" in get_db() {"));
		sList.add(new Statement(serial+8000L,2,"if "+this.domain.getSnakeDomainName()+"."+this.domain.getDomainId().getSnakeFieldName()+" > max {"));
		sList.add(new Statement(serial+9000L,3,"max = "+this.domain.getSnakeDomainName()+"."+this.domain.getDomainId().getSnakeFieldName()+";"));
		sList.add(new Statement(serial+10000L,2,"}"));
		sList.add(new Statement(serial+11000L,1,"}"));
		sList.add(new Statement(serial+12000L,1,"return max;"));
		sList.add(new Statement(serial+13000L,0,"}"));
		sList.add(new Statement(serial+14000L,0,""));
		
		serial += 15000L;
		Map<String,Set<Method>> methodsMap = MethodUtil.divideMethodSetWithTempTag(this.methods, this.domain.getCapFirstDomainNameWithSuffix());
		for (String key: methodsMap.keySet()) {
			Set<Method> mset = methodsMap.get(key);
			sList.add(new Statement(serial,0,"impl<'c> Table<'c, "+key+"> {"));
			serial += 1000L;
			Iterator it2 = mset.iterator();
			while(it2.hasNext()){
				Method m = (Method)it2.next();
				StatementList msl = m.generateMethodFullStatements();
				msl.setSerial(serial);
				msl.setIndent(1);
				sList.add(msl);
				serial += 1000L;
			}
			sList.add(new Statement(serial,0,"}"));
			sList.add(new Statement(serial+1000L,0,""));
			serial += 2000L;
		}		
		return WriteableUtil.merge(sList);
	}
	
	public StatementList generateEnumDummyDaoImplStatements() throws ValidateException{
		List<Writeable> sList = new ArrayList<Writeable>();
		
		Set<String> imports = this.generateImportStrings();
		imports.add("super::Table");
		imports.add("super::"+this.domain.getCapFirstDomainNameWithSuffix());
		imports.add("super::"+this.domain.getCapFirstDomainName()+"QueryRequest");
		imports.add("super::CountNum");
		imports.add("lazy_static::lazy_static");
		imports.add("std::sync::Mutex");
		imports.add("serde::{Deserialize, Serialize}");
		
		for (ManyToMany mtm : this.domain.getManyToManies()) {
			FindById findSlave = new FindById(mtm.getSlave());
			imports.add("crate::"+this.domain.getServiceimplSuffix()+"::"+mtm.getSlave().getSnakeDomainName()+"_service::"+StringUtil.getSnakeName(findSlave.getVerbName()));
		}

		if (this.domain.containsDateTime()) {
			imports.add("chrono::NaiveDate");
			imports.add("chrono::NaiveDateTime");
			imports.add("crate::"+this.domain.getDomainSuffix()+"::my_date_time_format::DATE_TIME_FORMAT");
			imports.add("crate::"+this.domain.getDomainSuffix()+"::my_date_format::DATE_FORMAT");
		}
		
		if ("Oracle".equalsIgnoreCase(this.domain.getDbType())) {
			imports.add("oracle::Error");
		}else {
			imports.add("sqlx::Error");
		}
		
		imports.addAll(this.classImports);
		
		sList.add(new Statement(100L,0,"#![allow(unused_imports,dead_code,unused_mut)]"));
		StatementList sl = DomainUtil.generateImportStatements(imports);
		sl.setSerial(1000L);
		sl.setIndent(0);
		sList.add(sl);
		
		sList.add(new Statement(6000L,0,"lazy_static! {"));
		sList.add(new Statement(7000L,1,"static ref DB:Mutex<Vec<"+this.domain.getCapFirstDomainNameWithSuffix()+">> = Mutex::new(init_db());"));
		long serial = 8000L;
		
		sList.add(new Statement(serial,0,"}"));
		sList.add(new Statement(serial+500L,0,""));
		
		sList.add(new Statement(serial+1000L,0,"fn init_db()->Vec<"+this.domain.getCapFirstDomainNameWithSuffix()+">{"));
		sList.add(new Statement(serial+2000L,1,"let mut _db:Vec<"+this.domain.getCapFirstDomainNameWithSuffix()+"> = vec![];"));
		serial += 3000L;
		for (Domain d: this.domain.getDummyDb()) {
			sList.add(new Statement(serial,1,"_db.push("+this.domain.getCapFirstDomainNameWithSuffix()+" {"));
			serial += 1000L;
			for (Field f: d.getFields()) {
				sList.add(new Statement(serial,2,f.getSnakeFieldName()+": "+FieldUtil.findTypeValueToken(f)+","));
				serial += 1000L;
			}
			sList.add(new Statement(serial,1,"});"));
			serial += 1000L;
		}		
		sList.add(new Statement(serial,1,"_db"));
		sList.add(new Statement(serial+1000L,0,"}"));
		sList.add(new Statement(serial+2000L,0,""));
		
		serial += 3000L;
			
		sList.add(new Statement(serial+1000L,0,"pub fn get_db()-> Vec<"+this.domain.getCapFirstDomainNameWithSuffix()+"> {"));
		sList.add(new Statement(serial+2000L,1,"DB.lock().unwrap().to_vec()"));
		sList.add(new Statement(serial+3000L,0,"}"));
		sList.add(new Statement(serial+4000L,0,""));
		
		sList.add(new Statement(serial+5000L,0,"fn max_id()->i64{"));
		sList.add(new Statement(serial+6000L,1,"let mut max:i64 = 0;"));
		sList.add(new Statement(serial+7000L,1,"for "+this.domain.getSnakeDomainName()+" in get_db() {"));
		sList.add(new Statement(serial+8000L,2,"if "+this.domain.getSnakeDomainName()+"."+this.domain.getDomainId().getSnakeFieldName()+" > max {"));
		sList.add(new Statement(serial+9000L,3,"max = "+this.domain.getSnakeDomainName()+"."+this.domain.getDomainId().getSnakeFieldName()+";"));
		sList.add(new Statement(serial+10000L,2,"}"));
		sList.add(new Statement(serial+11000L,1,"}"));
		sList.add(new Statement(serial+12000L,1,"return max;"));
		sList.add(new Statement(serial+13000L,0,"}"));
		sList.add(new Statement(serial+14000L,0,""));
		
		serial += 15000L;
		Map<String,Set<Method>> methodsMap = MethodUtil.divideMethodSetWithTempTag(this.methods, this.domain.getCapFirstDomainNameWithSuffix());
		for (String key: methodsMap.keySet()) {
			Set<Method> mset = methodsMap.get(key);
			sList.add(new Statement(serial,0,"impl<'c> Table<'c, "+key+"> {"));
			serial += 1000L;
			Iterator it2 = mset.iterator();
			while(it2.hasNext()){
				Method m = (Method)it2.next();
				StatementList msl = m.generateMethodFullStatements();
				msl.setSerial(serial);
				msl.setIndent(1);
				sList.add(msl);
				serial += 1000L;
			}
			sList.add(new Statement(serial,0,"}"));
			sList.add(new Statement(serial+1000L,0,""));
			serial += 2000L;
		}		
		return WriteableUtil.merge(sList);
	}
}
